#ifndef __GRAPHIC_HPP
#define __GRAPHIC_HPP

#include "../../utils/DoubleParamMap.hpp"
#include "../../utils/EasyMatrix.hpp"
#include "../../utils/FastMath.hpp"
#include <mutex>
#include <vector>

#include <iostream>
typedef int   Integer;
typedef float Float;

namespace model { namespace base{

namespace graphic {

class _3D {
	public:virtual void exposure(const utils::EasyMatrix<Float>& camera) = 0;
};


template<typename T> class Point {
	public:T x, y, z;
	
	public:Point(void): x(1), y(1), z(1) {}
	public:Point(T _x, T _y): x(_x), y(_y), z(1) {}
	public:Point(T _x, T _y, T _z): x(_x), y(_y), z(_z) {}
	
	public:void translate(const utils::EasyMatrix<Float>& matrix) {
		translate(*this, matrix);
	}
	
 	public:static void translate(Point& point, const utils::EasyMatrix<Float>& matrix) {

  		using utils::EasyMatrix;

 		int w = matrix.getWidth();
 		EasyMatrix<Float> self;
 		
 		if(w == 3) {
 			self = EasyMatrix<Float>(3, 1, {(Float) point.x,(Float) point.y, (Float) point.z});
 		} else if(w == 4) {
			self = EasyMatrix<Float>(4, 1, {(Float) point.x,(Float) point.y, (Float) point.z, 1.0F});
 		} else throw "Invalid translation matrix.";
		//std::cout<<self<<std::endl;
 		self = matrix * self;
		//std::cout<<self<<std::endl;
		point.x = self[0][0];
		point.y = self[1][0];
		point.z = self[2][0];
 	}
};


/**
 * 图形像素类：包含像素点及其位置信息 
 */ 
template<class PixelType> class Pixel {
	private:Point<Integer>     xy;
	private:PixelType pixel;
	
	public:Pixel(int x, int y, PixelType p): xy{x, y}, pixel(p) {	
	}
	
	public:Pixel(int x, int y): xy{x, y} {	
	}
	
	public:Pixel(void): xy{0, 0} {
	}
	
	public:void      setX(int _x)               { xy.x = _x; }
	public:void      setY(int _y)               { xy.y = _y; }
	public:void      setPixel(PixelType _pixel) { pixel = _pixel; }
	public:int       getX(void)     const       { return xy.x; }
	public:int       getY(void)     const       { return xy.y; }
	public:PixelType getPixel(void) const       { return pixel; }
	
	public:Pixel translate(const utils::EasyMatrix<Float>& matrix) {
		Point<Integer>::translate(xy, matrix);
		return *this;
	}
};
};

/**
 * 几何图形接口 
 * @param PixelType 像素类型 
 */
template<class PixelType> class Graphic {
	//#define VECTORBUFFER
	#define ARRAYBUFFER
	public:   typedef graphic::Pixel<PixelType>              Pixel;
	protected:typedef utils::DoubleParamMap<Integer, Integer, Pixel> Map;
	protected:typedef std::vector<Pixel>                     Vector;
	//----------------------------------------------------------------
	private:  mutable  std::mutex mtx;
	protected:unsigned pixelCount;  //像素点个数
	protected:Map*     pixelMap;    //像素点映射 
	private:  bool     visible;
	
	#if defined(VECTORBUFFER)
		protected:mutable  Vector*  pixelVector; //像素点缓冲向量
		
		public:Graphic(void): pixelCount(0), pixelMap(new Map()), pixelVector(new Vector) {
		}
		
		/**
		 * 清空图形 
		 */
		public:void clear(void) {
			std::lock_guard<std::mutex> lock(mtx);
			pixelMap->clear();
			if(buffered()) pixelVector->clear();
			pixelCount = 0;
		}
		
		/**
		 * 缓冲图形 
		 */
		public:const Pixel* buff(void) const {
			if(buffered()) return pixelVector->data();
			return rebuff();
		}
		
		/**
		 * 更新缓冲
		 */
		public:const Pixel* rebuff(void) const {
			if(pixelMap == nullptr  ||  pixelVector == nullptr) return nullptr;
			std::lock_guard<std::mutex> lock(mtx);
			pixelVector->clear();
			pixelMap->buff(*pixelVector);
			return pixelVector->data();
		}
		
		public:void putPixel(const Pixel& pixel) {
			if(pixelMap == nullptr  ||  pixelVector == nullptr) return;
			std::lock_guard<std::mutex> lock(mtx);
			pixelMap->put(pixel.getX(), pixel.getY(), pixel);
			if(buffered()) pixelVector->push_back(pixel);
			pixelCount = pixelMap->size();
		}
		
		public:void erasePixel(const Pixel& pixel) {
			if(pixelMap == nullptr  ||  pixelVector == nullptr) return;
			std::lock_guard<std::mutex> lock(mtx);
			pixelMap->erase(pixel.getX(), pixel.getY());
			if(buffered()) rebuff();
			pixelCount = pixelMap->size();
		}
		
		public:const Pixel* getBuffer() const {
			if(!buffered()) return nullptr;
			std::lock_guard<std::mutex> lock(mtx);
			return pixelVector->data();
		}
		
		public:bool buffered(void) const { 
			if(pixelVector != nullptr  &&  pixelVector->size() > 0) return true; 
			else return false;
		} 
	#elif defined(ARRAYBUFFER)
		protected:mutable  Pixel*  pixelVector; //像素点缓冲向量
		
		public:Graphic(void): pixelCount(0), pixelMap(new Map()), pixelVector(nullptr) {
		}
		
		/**
		 * 清空图形 
		 */
		public:void clear(void) {
			std::lock_guard<std::mutex> lock(mtx);
			pixelMap->clear();
			if(buffered()) {
				delete pixelVector;
				pixelVector = nullptr;
			}
			pixelCount = 0;
		}
		
		/**
		 * 缓冲图形 
		 */
		public:const Pixel* buff(void) const {
			if(buffered()) return pixelVector;
			return rebuff();
		}
		
		/**
		 * 更新缓冲
		 */
		public:const Pixel* rebuff(void) const {
			if(pixelMap == nullptr) return nullptr;
			
			std::lock_guard<std::mutex> lock(mtx);
			if(buffered()) delete pixelVector;
			
			pixelVector = new Pixel[pixelMap->size()];
			
			pixelMap->buff(pixelVector);
			return pixelVector;
		}
		
		public:void putPixel(const Pixel& pixel) {
			if(pixelMap == nullptr) return;
			std::lock_guard<std::mutex> lock(mtx);
			pixelMap->put(pixel.getX(), pixel.getY(), pixel);
			pixelCount = pixelMap->size();
			if(buffered()) rebuff();
		}
		
		public:void erasePixel(const Pixel& pixel) {
			if(pixelMap == nullptr) return;
			std::lock_guard<std::mutex> lock(mtx);
			pixelMap->erase(pixel.getX(), pixel.getY());
			pixelCount = pixelMap->size();
			if(buffered()) rebuff();
		}
		
		public:const Pixel* getBuffer() const {
			if(!buffered()) return nullptr;
			std::lock_guard<std::mutex> lock(mtx);
			return pixelVector;
		}
		
		public:bool buffered(void) const { 
			return pixelVector != nullptr;
		} 
	#else
		#error Please define ARRAYBUFFER or VECTORBUFFER flag.
	#endif
	//----------------------------------------------------------------
	
	public:Graphic(const Graphic& another) = delete; //阻止拷贝 
	
	public:Graphic(Graphic && another) noexcept: 
		pixelCount(another.pixelCount), pixelMap(another.pixelMap), pixelVector(another.pixelVector) {
		another.unuse();
	}

	public:Graphic& operator = (Graphic && another) noexcept {
		pixelCount = another.pixelCount; pixelMap = another.pixelMap; pixelVector = another.pixelVector;
		another.unuse();
		return *this;
	}
	
	private:void unuse(void) {
		pixelMap = nullptr;
		pixelVector = nullptr;
	}
	
	public:~Graphic(void) {
		delete pixelMap;
		delete pixelVector;
	}
	
	public:unsigned getPixelCount(void) {
		std::lock_guard<std::mutex> lock(mtx);
		return pixelCount;
	}
	
	public:unsigned getPixelCount(void) const {
		return pixelCount;
	}
	
	public:bool isVisible(void) const {
		return visible;
	}
	
	public:void setVisible(bool vsb) {
		visible = vsb;
	}
	
	public:static Integer getDelta(Integer y2, Integer y1) {
		return y2 - y1;
	}

	public:static Float getK(Integer x1, Integer y1, Integer x2, Integer y2) {
		return (Float)getDelta(y2, y1) / (Float)getDelta(x2, x1);
	}
	
	public:virtual Graphic& translate(const utils::EasyMatrix<Float>& matrix) = 0;
	public:Graphic& operator << (const utils::EasyMatrix<Float>& matrix) {
		return translate(matrix);
	}
	
	//以下三个是获取相应图形变换矩阵的代码
	
	public:static utils::EasyMatrix<Float> getPositionTranslate3x3F(Float x, Float y) {
		return utils::EasyMatrix<Float>(3, 3, {
			1.0, 0.0, x,
			0.0, 1.0, y,
			0.0, 0.0, 1.0
		});
	}
	
	public:static utils::EasyMatrix<Float> getScaleTranslate3x3F(Float x, Float y) {
		return utils::EasyMatrix<Float>(3, 3, {
			x,   0.0, 0.0,
			0.0, y,   0.0,
			0.0, 0.0, 1.0
		});
	}
	
	public:static utils::EasyMatrix<Float> getRotationTranslate3x3F(Float theta) {
		Float cosTheta = utils::FastMath::fastMath.cos(theta);
		Float sinTheta = utils::FastMath::fastMath.sin(theta);
		return utils::EasyMatrix<Float>(3, 3, {
			cosTheta, -sinTheta, 0.0,
			sinTheta,  cosTheta, 0.0,
			0.0,       0.0,      1.0
		});
	}
	
	public:static utils::EasyMatrix<Float> getPositionTranslate4x4F(Float x, Float y, Float z) {
		return utils::EasyMatrix<Float>(4, 4, {
			1.0, 0.0, 0.0, x,
			0.0, 1.0, 0.0, y,
			0.0, 0.0, 1.0, z,
			0.0, 0.0, 0.0, 1.0
		});
	}
	public:static utils::EasyMatrix<Float> getScaleTranslate4x4F(Float x, Float y, Float z) {
		return utils::EasyMatrix<Float>(4, 4, {
			x  , 0.0, 0.0, 0.0,
			0.0, y  , 0.0, 0.0,
			0.0, 0.0, z  , 0.0,
			0.0, 0.0, 0.0, 1.0
		});
	}
	public:static utils::EasyMatrix<Float> getCamera4x4F(Float a, Float b, Float c, Float d) {
		return utils::EasyMatrix<Float>(4, 4, {
			1.0F, 0.0F, 0.0F, 0.0F,
			0.0F, 1.0F, 0.0F, 0.0F,
			0.0F, 0.0F, 1.0F, 0.0F,
			a,    b,    c,    d,
		});
	}
	public:enum RollBy {XAxis, YAxis, ZAxis};
	public:static utils::EasyMatrix<Float> getRotationTranslate4x4F(RollBy rollBy, Float theta) {
		Float cosTheta = utils::FastMath::fastMath.cos(theta);
		Float sinTheta = utils::FastMath::fastMath.sin(theta);
		switch(rollBy) {
			case XAxis:
				return utils::EasyMatrix<Float>(4, 4, {
	  				1.0, 0.0,       0.0,      0.0,
					0.0, cosTheta, -sinTheta, 0.0,
					0.0, sinTheta,  cosTheta, 0.0,
					0.0, 0.0,       0.0,      1.0
				});
				
			case YAxis:
				return utils::EasyMatrix<Float>(4, 4, {
				    cosTheta, 0.0, sinTheta, 0.0,
				    0.0,      1.0, 0.0,      0.0,
				   -sinTheta, 0.0, cosTheta, 0.0,
				    0.0,      0.0, 0.0,      1.0
				});
				
			case ZAxis:
				return utils::EasyMatrix<Float>(4, 4, {
					cosTheta, -sinTheta, 0.0, 0.0,
					sinTheta, cosTheta,  0.0, 0.0,
					0.0,      0.0,       1.0, 0.0,
					0.0,      0.0,       0.0, 1.0
				});
		}
		return utils::EasyMatrix<Float>(4, 4, {
			1.0, 0.0, 0.0, 0.0,
			0.0, 1.0, 0.0, 0.0,
			0.0, 0.0, 1.0, 0.0,
			0.0, 0.0, 0.0, 1.0
		});
	}
	
	
};

};};
#endif
